//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Workflow.ComponentModel;

namespace Microsoft.Research.ScientificWorkflow.Activities
{
    [Name("Web Data Reader Async")]
    [Description("Given a list of URL's this method downloads files from those locations.")]
    [WorkflowCategory("Web")]
    public class DownloadWebDataAsync : Activity
    {
        #region Dependency Properties

        public static DependencyProperty URLArrayProperty = DependencyProperty.Register("URLArray",
            typeof(Uri[]), typeof(DownloadWebDataAsync));

        [RequiredInputParam]
        [Name("URLArray")]
        [Description("URL array containing files to be downloaded.")]
        public Uri[] URLArray
        {
            get
            {
                return ((Uri[])(base.GetValue(DownloadWebDataAsync.URLArrayProperty)));
            }
            set
            {
                base.SetValue(DownloadWebDataAsync.URLArrayProperty, value);
            }
        }

        public static DependencyProperty DownloadDirProperty = DependencyProperty.Register("DownloadDir",
            typeof(DirectoryInfo), typeof(DownloadWebDataAsync));

        [RequiredInputParam]
        [Name("DownloadDir")]
        [Description("Directory where files would be downloaded.")]
        public DirectoryInfo DownloadDir
        {
            get
            {
                return ((DirectoryInfo)(base.GetValue(DownloadWebDataAsync.DownloadDirProperty)));
            }
            set
            {
                base.SetValue(DownloadWebDataAsync.DownloadDirProperty, value);
            }
        }

        #endregion

        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            if (null == this.URLArray || this.URLArray.Length == 0 ||
                null == this.DownloadDir)
                return ActivityExecutionStatus.Faulting;
            
            HttpWebRequest_BeginGetResponse.DownloadFiles(this.URLArray, this.DownloadDir.Name);

            return ActivityExecutionStatus.Closed;
        }
    }

    public class RequestState
    {
        // This class stores the State of the request.
        const int BUFFER_SIZE = 1024;
        public StringBuilder requestData;
        public byte[] BufferRead;
        public HttpWebRequest request;
        public HttpWebResponse response;
        public Stream streamResponse;
        public RequestState()
        {
            BufferRead = new byte[BUFFER_SIZE];
            requestData = new StringBuilder("");
            request = null;
            streamResponse = null;
        }
    }

    class HttpWebRequest_BeginGetResponse
    {
        const int BUFFER_SIZE = 1024;
        const int DefaultTimeout = 2 * 60 * 1000; // 2 minutes timeout
        private static int fileCount;
        private static int filenumber = 0;
        private static Object[] NumImagesMutex = new Object[0];
        private static Object[] WaitObject = new Object[0];
        private static string folderName;

        public static void DownloadFiles(Uri[] filesToDownload, string folder)
        {  
            try
            {
                fileCount = filesToDownload.Length;
                folderName = folder;
                foreach(Uri queryStr in filesToDownload)
                {
                    // Create a HttpWebrequest object to the desired URL. 
                    HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(queryStr);

                    // Create an instance of the RequestState and assign the previous myHttpWebRequest
                    // object to its request field.  
                    RequestState myRequestState = new RequestState();  
                    myRequestState.request = myHttpWebRequest;

                    // Start the asynchronous request.
                    IAsyncResult result=
                    (IAsyncResult) myHttpWebRequest.BeginGetResponse(new AsyncCallback(RespCallback),myRequestState);
                }

                bool mustLock = false;
                lock (NumImagesMutex)
                {
                    if (fileCount > 0)
                        mustLock = true;
                }
                if (mustLock)
                {
                    Monitor.Enter(WaitObject);
                    Monitor.Wait(WaitObject);
                    Monitor.Exit(WaitObject);
                }

            }
            catch(WebException e)
            {
                Console.WriteLine("\nMain Exception raised!");
                Console.WriteLine("\nMessage:{0}",e.Message);
                Console.WriteLine("\nStatus:{0}",e.Status);
                Console.WriteLine("Press any key to continue..........");
            }
            catch(Exception e)
            {
                Console.WriteLine("\nMain Exception raised!");
                Console.WriteLine("Source :{0} " , e.Source);
                Console.WriteLine("Message :{0} " , e.Message);
                Console.WriteLine("Press any key to continue..........");
                Console.Read();
            }
        }

        private static void RespCallback(IAsyncResult asynchronousResult)
        {
            try
            {
                // State of request is asynchronous.
                RequestState myRequestState = (RequestState)asynchronousResult.AsyncState;
                HttpWebRequest myHttpWebRequest = myRequestState.request;
                myRequestState.response = (HttpWebResponse)myHttpWebRequest.EndGetResponse(asynchronousResult);

                // Read the response into a Stream object.
                Stream responseStream = myRequestState.response.GetResponseStream();
                myRequestState.streamResponse = responseStream;

                // Begin the Reading of the contents of the HTML page and print it to the console.
                IAsyncResult asynchronousInputRead = responseStream.BeginRead(myRequestState.BufferRead, 0, 
                    BUFFER_SIZE, new AsyncCallback(ReadCallBack), myRequestState);
                return;
            }
            catch (WebException e)
            {
                Console.WriteLine("\nRespCallback Exception raised!");
                Console.WriteLine("\nMessage:{0}", e.Message);
                Console.WriteLine("\nStatus:{0}", e.Status);
            }
        }

        private static void ReadCallBack(IAsyncResult asyncResult)
        {
            try
            {

                RequestState myRequestState = (RequestState)asyncResult.AsyncState;
                Stream responseStream = myRequestState.streamResponse;
                int read = responseStream.EndRead(asyncResult);
                // Read the HTML page and then print it to the console.
                if (read > 0)
                {
                    myRequestState.requestData.Append(Encoding.ASCII.GetString(myRequestState.BufferRead, 0, read));
                    IAsyncResult asynchronousResult = responseStream.BeginRead(myRequestState.BufferRead, 0, BUFFER_SIZE, 
                        new AsyncCallback(ReadCallBack), myRequestState);
                    return;
                }
                else
                {
                    if (myRequestState.requestData.Length > 1)
                    {
                        string fileName = folderName + "\\File" + (filenumber++);
                        File.WriteAllText(fileName, myRequestState.requestData.ToString());
                    }
                    responseStream.Close();
                }
                lock (NumImagesMutex)
                {
                    fileCount--;
                    if (fileCount == 0)
                    {
                        Monitor.Enter(WaitObject);
                        Monitor.Pulse(WaitObject);
                        Monitor.Exit(WaitObject);
                    }
                }
            }
            catch (WebException e)
            {
                Console.WriteLine("\nReadCallBack Exception raised!");
                Console.WriteLine("\nMessage:{0}", e.Message);
                Console.WriteLine("\nStatus:{0}", e.Status);
            }
        }
    }
}
